# 典型的"异常处理机制"或者叫"中断处理机制"，在自动化测试和机器人流程自动化(RPA)中非常常见。下面我为你提供一个基本的框架实现
import base64
import os.path
import time
import os
from types import SimpleNamespace
import json5
from enum import Enum
from typing import List, Callable, Optional, Union, Dict
from dataclasses import dataclass
from abc import ABC, abstractmethod
import numpy as np
import random
import re
from yangke.common.config import logger


class InputInterface(ABC):
    """
    抽象输入接口类，定义了键鼠操作的统一接口
    """

    def __init__(self):

        self.initialized = False
        self.root_path = os.getcwd()
        try:
            from paddleocr import PaddleOCR
            self.ocr_instance = PaddleOCR(
                use_doc_orientation_classify=False,
                use_doc_unwarping=False,
                use_textline_orientation=False)  # 文本检测 + 文本识别
        except:
            logger.debug(f"未安装paddleocr，不支持文本识别")
            self.ocr_instance = None

    def get_base64_image_size(self, base64_string: str) -> tuple:
        """
        获取base64图片的宽和高

        参数:
        - base64_string: base64编码的图像数据（可能包含data URI前缀）

        返回:
        - tuple: (width, height) 图片的宽度和高度
        """
        try:
            import cv2

            # 如果base64字符串包含data URI前缀，则移除它
            if base64_string.startswith('data:'):
                # 找到base64数据的开始位置
                base64_data = base64_string.split(',', 1)[1]
            else:
                base64_data = base64_string

            # 解码base64字符串
            image_data = base64.b64decode(base64_data)

            # 将字节数据转换为numpy数组
            nparr = np.frombuffer(image_data, np.uint8)
            # 解码图像
            img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)
            # 获取图像尺寸
            height, width = img.shape[:2]
            return width, height
        except Exception as e:
            logger.error(f"获取base64图像尺寸失败: {e}")
            return 0, 0

    def save_base64_to_file(self, base64_string: str, file_path: str = 'tmp.png') -> bool:
        """
        将base64字符串保存为图片文件，支持bmp, png, jpg格式

        参数:
        - base64_string: base64编码的图像数据（可能包含data URI前缀）
        - file_path: 保存的文件路径

        返回:
        - bool: 是否保存成功
        """
        try:
            # 如果base64字符串包含data URI前缀，则移除它
            if base64_string.startswith('data:'):
                # 找到base64数据的开始位置
                base64_data = base64_string.split(',', 1)[1]
            else:
                base64_data = base64_string

            # 解码base64字符串
            image_data = base64.b64decode(base64_data)

            # 写入文件
            with open(file_path, 'wb') as f:
                f.write(image_data)

            return True
        except Exception as e:
            logger.error(f"保存base64图像到文件失败: {e}")
            return False

    def crop_base64_image(self, base64_string: str, x1: int, y1: int, x2: int, y2: int) -> str:
        """
        将base64图片按照指定区域进行裁剪，裁剪后仍保存为base64格式

        参数:
        - base64_string: base64编码的图像数据（可能包含data URI前缀）
        - x1, y1, x2, y2: 裁剪区域的坐标 (左上角x1,y1, 右下角x2,y2)

        返回:
        - str: 裁剪后的base64图像数据（包含data URI前缀）
        """
        try:
            import cv2

            # 如果base64字符串包含data URI前缀，则保留前缀信息
            if base64_string.startswith('data:'):
                # 提取前缀信息
                prefix = base64_string.split(',', 1)[0]
                base64_data = base64_string.split(',', 1)[1]
            else:
                prefix = 'data:image/png;base64'
                base64_data = base64_string

            # 解码base64字符串
            image_data = base64.b64decode(base64_data)

            # 将字节数据转换为numpy数组
            nparr = np.frombuffer(image_data, np.uint8)
            # 解码图像
            img = cv2.imdecode(nparr, cv2.IMREAD_UNCHANGED)

            # 裁剪图像
            cropped_img = img[y1:y2, x1:x2]

            # 将裁剪后的图像编码为base64
            _, buffer = cv2.imencode('.png', cropped_img)
            cropped_base64 = base64.b64encode(buffer).decode('utf-8')

            # 返回带前缀的base64字符串
            return f"{prefix},{cropped_base64}"
        except Exception as e:
            logger.error(f"裁剪base64图像失败: {e}")
            return ""

    def ocr(self, pic_png: str, target_str: str = None, save_img=False, exact=False, score=0.8):
        """
        对图像进行OCR识别。找到一个文字，则返回(文字字符串, (左上x, 左上y, 右下x, 右下y), 可信度评分)。如果找到多处文字，则返回前面格式的列表，未找到返回None
        :param pic_png: 可以是data:开头的base64字符串或这png文件的路径
        :param target_str: 如果传入，则返回目标字符串的位置和可信度评分，如果不传入，则返回所有识别的信息
        :param save_img: 如果为True，则保存标注后的图片文字信息，即原图上会标注出识别到的文字
        :param exact: 是否精准匹配，True，严格匹配文字=target_str，否则匹配文字包含target_str
        :param score: 置信度
        :return:
        """
        if score is None:
            score = 0.8
        if pic_png.startswith('data:'):  # base64字符串转png图片
            self.save_base64_to_file(pic_png, 'tmp.png')
            pic_png = 'tmp.png'
        result = self.ocr_instance.predict(pic_png)
        output = []
        for res in result:
            if save_img:
                res.save_to_img("output")
            # 将res['rec_boxes']中的ndarray转换为list，以便结果可以通过json进行序列化
            p1p2list = [[int(i) for i in p1p2] for p1p2 in res['rec_boxes']]
            output.extend(tuple(zip(res['rec_texts'], p1p2list, res['rec_scores'])))

        # 从output中删除res_scores小于score的元素
        output = [x for x in output if x[2] > score]
        if target_str is None:
            output1 = [x for x in output]
        else:
            if exact:
                output1 = [x for x in output if x[0] == target_str]
            else:
                output1 = [x for x in output if target_str in x[0]]
        if len(output1) > 1:
            return output1
        elif len(output1) == 1:
            return output1[0]
        else:
            return None

    @abstractmethod
    def find_pic(self, pic, sim=0.9, direction: int = 0, x1=0, y1=0, x2=0, y2=0, snapshot=None):
        """
        查找图片在
        :param pic: 查找的小图片，一般为目标图片，支持路径
        :param sim:
        :param direction:
        :param x1:
        :param y1:
        :param x2:
        :param y2:
        :param snapshot: 查找的大图片，一般为屏幕截图，支持路径或base64字符串，如果不传入，则默认为全屏或全窗口画面查找
        :return: {'exists': True, 'pos': (718, 656)}
        """
        pass

    @abstractmethod
    def capture(self, x1: int = 0, y1: int = 0, x2: int = 0, y2: int = 0, file: str = 'tmp.bmp') -> dict:
        """
        截图并保存为文件，返回{"suc": True, "image": base64_str, "image_path": bmp_path}
        """
        pass

    @abstractmethod
    def left_click(self, x: int, y: int):
        """
        鼠标左键点击
        """
        pass

    @abstractmethod
    def middle_click(self, x: int, y: int):
        pass

    @abstractmethod
    def right_click(self, x: int, y: int):
        """
        鼠标右键点击
        """
        pass

    def left_down(self, x: int, y: int):
        pass

    def left_up(self):
        pass

    def right_down(self, x: int, y: int):
        pass

    def right_up(self):
        pass

    def middle_down(self, x: int, y: int):
        pass

    def middle_up(self):
        pass

    def wheel_up(self):
        pass

    def wheel_down(self):
        pass

    @abstractmethod
    def press_key_combination(self, key_combination: str):
        pass

    @abstractmethod
    def move_to(self, x: int, y: int):
        """
        鼠标移动到指定位置
        """
        pass

    @abstractmethod
    def press_key(self, key: str):
        """
        按下键盘按键
        """
        pass

    @abstractmethod
    def key_down(self, key: str):
        """
        按下键盘按键不释放
        """
        pass

    @abstractmethod
    def key_up(self, key: str):
        """
        释放键盘按键
        """
        pass

    @abstractmethod
    def send_string(self, string):
        pass

    def SetWindowState(self, hwnd, flag):
        pass

    @abstractmethod
    def send_string_ime(self, string):
        pass

    @abstractmethod
    def send_string_ime2(self, string):
        pass

    @abstractmethod
    def key_press_str(self, string, delay_ms=100):
        pass

    @abstractmethod
    def set_clipboard(self, string):
        pass

    @abstractmethod
    def send_paste(self):
        pass


class PyAutoGUIInput(InputInterface):
    """
    PyAutoGUI输入实现类
    """

    def __init__(self):
        import pyautogui
        self.pyautogui = pyautogui

    def capture(self, x1: int = 0, y1: int = 0, x2: int = 0, y2: int = 0, file: str = 'tmp.bmp') -> dict:
        self.pyautogui.screenshot(file, region=(x1, y1, x2 - x1, y2 - y1))
        return {"suc": True, "image": file}

    def left_click(self, x: int, y: int):
        self.pyautogui.click(x, y, button='left')

    def right_click(self, x: int, y: int):
        self.pyautogui.click(x, y, button='right')

    def move_to(self, x: int, y: int):
        self.pyautogui.moveTo(x, y)

    def press_key(self, key: str):
        self.pyautogui.press(key)

    def key_down(self, key: str):
        self.pyautogui.keyDown(key)

    def key_up(self, key: str):
        self.pyautogui.keyUp(key)


@dataclass
class InterruptionPattern:
    """
    中断模式定义类，用于描述一种可能的中断情况及其处理方式

    属性说明:
    - name: 中断模式的名称，用于标识和日志记录
    - detection_method: 检测方法，是一个可调用对象，返回布尔值
                       True表示检测到该中断情况，False表示未检测到
    - action: 处理动作，是一个可调用对象，返回布尔值
             True表示中断处理成功，False表示处理失败
    - priority: 优先级，整数值，数值越大优先级越高
               在处理中断时会按照优先级从高到低的顺序进行处理
    """
    name: str
    detection_method: Callable[[], bool]  # 检测方法，返回True表示检测到该中断
    action: Callable[[], bool]  # 处理动作，返回True表示处理成功
    priority: int = 0  # 优先级，数值越大优先级越高


class InterruptionHandler:
    """
    中断处理器类，负责管理和执行各种中断处理模式

    主要功能:
    1. 注册和注销中断处理模式
    2. 按优先级顺序检测和处理中断
    3. 提供启用/禁用中断处理的开关
    """

    def __init__(self):
        """
        初始化中断处理器
        - patterns: 存储所有已注册的中断模式列表
        - enabled: 中断处理开关，True表示启用中断处理，False表示禁用
        """
        self.patterns: List[InterruptionPattern] = []
        self.enabled = True

    def register_pattern(self, pattern: InterruptionPattern):
        """
        注册一个新的中断处理模式

        参数:
        - pattern: InterruptionPattern对象，包含中断的检测方法和处理动作

        处理流程:
        1. 将新模式添加到模式列表中
        2. 按优先级重新排序，确保高优先级的模式排在前面
        3. 记录日志信息
        """
        self.patterns.append(pattern)
        # 按优先级排序，优先级高的在前
        self.patterns.sort(key=lambda x: x.priority, reverse=True)
        logger.info(f"Registered interruption pattern: {pattern.name}")

    def unregister_pattern(self, name: str):
        """
        注销指定名称的中断处理模式

        参数:
        - name: 要注销的中断模式名称

        处理流程:
        1. 从模式列表中移除指定名称的模式
        2. 记录日志信息
        """
        self.patterns = [p for p in self.patterns if p.name != name]
        logger.info(f"Unregistered interruption pattern: {name}")

    def handle_interruptions(self) -> bool:
        """
        处理当前可能存在的中断

        返回值:
        - True: 表示处理了至少一个中断
        - False: 表示没有中断需要处理或中断处理被禁用

        处理流程:
        1. 检查中断处理是否启用
        2. 遍历所有已注册的中断模式（按优先级顺序）
        3. 对每个模式执行检测，如果检测到则执行相应的处理动作
        4. 记录处理过程中的日志信息
        5. 返回是否处理了至少一个中断
        """
        # 如果中断处理被禁用，则直接返回False
        if not self.enabled:
            return False

        # 标记是否处理了中断
        handled = False

        # 遍历所有中断模式，按优先级顺序处理
        for pattern in self.patterns:
            try:
                # 执行检测方法，检查是否出现该中断情况
                if pattern.detection_method():
                    logger.info(f"Detected interruption: {pattern.name}")

                    # 执行处理动作
                    if pattern.action():
                        logger.info(f"Successfully handled interruption: {pattern.name}")
                        handled = True  # 标记已处理中断
                    else:
                        logger.warning(f"Failed to handle interruption: {pattern.name}")
            except Exception as e:
                # 捕获处理过程中的异常，避免影响其他中断模式的处理
                logger.error(f"Error handling interruption {pattern.name}: {e}")

        return handled


class TaskContext:
    """
    任务上下文类，包含任务执行所需的所有上下文信息
    """

    def __init__(self, input_interface: InputInterface = None, interruption_handler: InterruptionHandler = None):
        self.input_interface = input_interface or None
        self.interruption_handler = interruption_handler or None
        self.last_condition = None  # 上一次的判断条件对象
        self.printToTerm: Callable[[str, str], None] | None = None
        self.setTaskBar: Callable[[str, int], None] | None = None

    def setLastCondition(self, condition):
        self.last_condition = condition

    def setPrint(self, printToTerm, setTaskBar):
        self.printToTerm = printToTerm
        self.setTaskBar = setTaskBar

    def set_input_interface(self, input_interface):
        self.input_interface = input_interface or self.input_interface

    def set_interruption_handler(self, interruption_handler):
        self.interruption_handler = interruption_handler or self.interruption_handler

    def debug(self, msg):
        if self.printToTerm:
            self.printToTerm(msg, 'debug')
        if self.setTaskBar:
            self.setTaskBar(msg, 0)
        logger.debug(msg)


class ConditionType(Enum):
    """条件类型枚举"""
    NONE = "none"
    IMAGE_EXISTS = "image_exists"  # 图像存在
    TEXT_EXISTS = "text_exists"  # 文本存在
    TEXT_EXISTS_EXACT = "text_exists_exact"  # 文本存在（精确匹配）
    IMAGE_NOT_EXISTS = "image_not_exists"  # 图像不存在
    TEXT_NOT_EXISTS = "text_not_exists"  # 文本不存在


class TaskCondition:
    """
    任务条件类，用于定义任务步骤的执行条件和预期结果

    属性说明:
    - condition_type: 条件类型
    - value: 条件值（图像路径或文本内容）
    - threshold: 识别阈值（针对图像识别）
    """

    def __init__(self, condition_type: ConditionType = None, value: str = None, threshold: float = 0.8):
        self.condition_type = condition_type
        self.region_str: str | None = None
        self.region: tuple[int, int, int, int] | None = None  # 截图的区域，对应x1, y1, x2, y2
        self.value = value
        self.threshold = threshold
        self.position = None  # 当条件为存在某个图片或文本时，这里存储找到的图片或文本的坐标
        self.next_step = None

    def __str__(self):
        return f"{self.condition_type.value}: {str(self.value)[:20]}"

    def region_str_2_coor(self, region_str: str, w: int, h: int):
        """
        region_str可能是以下几种格式，需要从中解析出具体的区域坐标[x1, y1, x2, y2]。w和h分别对应画面的宽和高，可以通过self.input.get_base64_image_size(screen)获得
        1. "[x1, y1, x2, y2]"
        2. "{x1: 10, y1: 10, x2: 100, y2: 300}"
        3. "x>100" # 表示截取x>100的这部分区域，区域坐标应当为[100, 0, w, h]
        4. "x>0.5w" # 表示截取画面的右边50%区域，区域坐标应当为[0.5w, 0, w, h]
        5. "x<0.4w" # 表示截取画面左侧的40%区域，区域坐标应当为[0, 0, 0.4w, h]
        6. "0.3w<x<0.6w" # 表示截取画面中间从0.3w至0.6w的区域，区域坐标应当为[0.3w, 0, 0.6w, h]
        7. "x>500, y<0.3h" # 表示截取画面右上部分区域，区域坐标应当为[500, 0, w, 0.3h]
        8. 其他类似的x和y的组合

        :param region_str: 描述区域的字符串
        :param w: 画面总宽度
        :param h: 画面总高度
        :return:
        """
        if region_str is None:
            return [0, 0, w, h]
        region_str = region_str.lower().strip()
        region_str = region_str.replace(' ', '')
        x1, y1, x2, y2 = 0, 0, w, h  # 默认值为全屏

        # 处理不同的格式
        if region_str.startswith('[') and region_str.endswith(']'):
            # 格式1: "[x1, y1, x2, y2]"
            coords = json5.loads(region_str)
            x1, y1, x2, y2 = coords[0], coords[1], coords[2], coords[3]
        elif region_str.startswith('{') and region_str.endswith('}'):
            # 格式2: "{x1: 10, y1: 10, x2: 100, y2: 300}"
            coords = json5.loads(region_str)
            x1, y1, x2, y2 = coords['x1'], coords['y1'], coords['x2'], coords['y2']
        else:
            # 处理表达式格式 (3-8)
            # 解析 x 方向的约束
            if 'x>' in region_str or 'x<' in region_str:
                # 处理类似 "x>100", "x>0.5w", "x<0.4w", "0.3w<x<0.6w"
                x_parts = [part.strip() for part in region_str.split(',')]
                x_constraints = [part for part in x_parts if 'x' in part]

                for constraint in x_constraints:
                    if '<' in constraint and '>' in constraint:
                        # 格式如 "0.3w<x<0.6w"
                        parts = constraint.split('<')
                        if 'x' in parts[1]:
                            # 形如 "0.3w<x<0.6w"
                            x1_expr = parts[0]
                            x2_expr = parts[2]
                        else:
                            # 形如 "x>0.3w" 和 "x<0.6w" 分开的情况会在下面处理
                            continue

                        # 计算 x1
                        if x1_expr.endswith('w'):
                            ratio = float(x1_expr[:-1]) if x1_expr[:-1] else 0
                            x1 = int(ratio * w)
                        else:
                            x1 = int(eval(x1_expr))

                        # 计算 x2
                        if x2_expr.endswith('w'):
                            ratio = float(x2_expr[:-1]) if x2_expr[:-1] else 1
                            x2 = int(ratio * w)
                        else:
                            x2 = int(eval(x2_expr))
                    elif 'x>' in constraint:
                        # 格式如 "x>100" 或 "x>0.5w"
                        expr = constraint.split('>')[-1]
                        if expr.endswith('w'):
                            ratio = float(expr[:-1]) if expr[:-1] else 0
                            x1 = int(ratio * w)
                        else:
                            x1 = int(eval(expr))
                    elif 'x<' in constraint:
                        # 格式如 "x<0.4w"
                        expr = constraint.split('<')[-1]
                        if expr.endswith('w'):
                            ratio = float(expr[:-1]) if expr[:-1] else 1
                            x2 = int(ratio * w)
                        else:
                            x2 = int(eval(expr))

            # 解析 y 方向的约束
            if 'y>' in region_str or 'y<' in region_str:
                # 处理类似 "y<0.3h"
                y_parts = [part.strip() for part in region_str.split(',')]
                y_constraints = [part for part in y_parts if 'y' in part and 'x' not in part]

                for constraint in y_constraints:
                    if 'y>' in constraint:
                        # 格式如 "y>100" 或 "y>0.5h"
                        expr = constraint.split('>')[-1]
                        if expr.endswith('h'):
                            ratio = float(expr[:-1]) if expr[:-1] else 0
                            y1 = int(ratio * h)
                        else:
                            y1 = int(eval(expr))
                    elif 'y<' in constraint:
                        # 格式如 "y<0.3h"
                        expr = constraint.split('<')[-1]
                        if expr.endswith('h'):
                            ratio = float(expr[:-1]) if expr[:-1] else 1
                            y2 = int(ratio * h)
                        else:
                            y2 = int(eval(expr))

        # 确保坐标合法
        x1 = max(0, min(x1, w))
        y1 = max(0, min(y1, h))
        x2 = max(0, min(x2, w))
        y2 = max(0, min(y2, h))

        # 确保 x1 < x2, y1 < y2
        if x1 > x2:
            x1, x2 = x2, x1
        if y1 > y2:
            y1, y2 = y2, y1
        return x1, y1, x2, y2

    def define_condition_by_json(self, json_data: dict):
        self.condition_type = ConditionType(json_data.get("condition_type") or json_data.get("conditionType"))
        self.value = json_data.get("value")
        self.threshold = json_data.get("threshold")
        self.next_step = json_data.get("next_step") or json_data.get("nextStep") or self.next_step
        self.region_str = json_data.get("region") or None

    def test(self, task_context: 'TaskContext'):
        """
        测试画面上的信息，如果为图片匹配，则标记查找到的图片，如果为文字识别，则标记识别的所有文字

        :param task_context:
        :return:
        """
        import cv2

        if self.condition_type == ConditionType.NONE:  # 任意条件都满足
            return True
        window_pic = task_context.input_interface.capture().get('image')
        w, h = task_context.input_interface.get_base64_image_size(window_pic)

        if self.region_str is not None and self.region_str != '':
            x1, y1, x2, y2 = self.region_str_2_coor(self.region_str, w, h)
        else:
            x1, y1, x2, y2 = 0, 0, w, h
        # 现在就有了判断的区域，即(x1, y1, x2, y2)，在图中标记出判断区域
        image_data = window_pic.split(',', 1)[1]  # 去除base64字符串的前缀
        image_bytes = base64.b64decode(image_data)
        image_array = np.frombuffer(image_bytes, np.uint8)
        img = cv2.imdecode(image_array, cv2.IMREAD_COLOR)
        # 绘制矩形框标记区域
        cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2)

        # 裁剪图片
        crop_image = task_context.input_interface.crop_base64_image(window_pic, x1, y1, x2, y2)
        if crop_image == '':
            logger.debug(f'裁剪的图片区域错误，请检查，当前为{x1, y1, x2, y2}')
            return
        check_res = False
        ocr_res = None
        # ------------------------------- 查看是否有匹配到图片或文字 -----------------------------------------
        if self.condition_type == ConditionType.IMAGE_EXISTS or self.condition_type == ConditionType.IMAGE_NOT_EXISTS:
            # 检查图像是否存在
            task_context.input_interface.save_base64_to_file(self.value, 'child_pic.png')  # 将文件保存到dm的运行目录下
            pos = task_context.input_interface.find_pic('child_pic.png', x1=x1, y1=y1, x2=x2, y2=y2,
                                                        snapshot=crop_image)
            if pos.get('exists'):
                w, h = task_context.input_interface.get_base64_image_size(self.value)  # 小图片的大小
                lux, luy = pos.get('pos')  # 图片的左上角坐标
                # 将截图区域的位置偏移添加到识别结果上去
                _x1, _y1 = lux + x1, luy + y1
                _x2, _y2 = lux + w + x1, luy + h + y1
                cv2.rectangle(img, (_x1, _y1), (_x2, _y2), (0, 0, 255), 2)  # 小图片的区域，也在图中标记出来
                check_res = True if self.condition_type == ConditionType.IMAGE_EXISTS else False
            else:
                check_res = False if self.condition_type == ConditionType.IMAGE_EXISTS else True
        elif self.condition_type == ConditionType.TEXT_EXISTS:
            # 检查文本是否存在
            ocr_res = task_context.input_interface.ocr(crop_image, score=self.threshold)
            check_res = False
            if ocr_res is not None:
                for item in ocr_res:
                    text, box, score = item
                    _x1, _y1, _x2, _y2 = box
                    _x1, _y1 = _x1 + x1, _y1 + y1  # 将截图区域的位置偏移添加到识别结果上去
                    _x2, _y2 = _x2 + x1, _y2 + y1
                    cv2.rectangle(img, (_x1, _y1), (_x2, _y2), (0, 0, 255), 2)  # 小图片的区域，也在图中标记出来
                    if self.value in text:
                        check_res = True
        elif self.condition_type == ConditionType.TEXT_NOT_EXISTS:
            # 检查文本是否存在
            ocr_res = task_context.input_interface.ocr(crop_image, score=self.threshold)
            check_res = True
            if ocr_res is not None:
                for item in ocr_res:
                    text, box, score = item
                    _x1, _y1, _x2, _y2 = box
                    _x1, _y1 = _x1 + x1, _y1 + y1  # 将截图区域的位置偏移添加到识别结果上去
                    _x2, _y2 = _x2 + x1, _y2 + y1
                    cv2.rectangle(img, (_x1, _y1), (_x2, _y2), (0, 0, 255), 2)  # 小图片的区域，也在图中标记出来
                    if text in self.value:
                        check_res = False
        elif self.condition_type == ConditionType.TEXT_EXISTS_EXACT:
            # 检查文本是否存在
            ocr_res = task_context.input_interface.ocr(crop_image, score=self.threshold)
            check_res = False
            if ocr_res is not None:
                for item in ocr_res:
                    text, box, score = item
                    _x1, _y1, _x2, _y2 = box
                    _x1, _y1 = _x1 + x1, _y1 + y1  # 将截图区域的位置偏移添加到识别结果上去
                    _x2, _y2 = _x2 + x1, _y2 + y1
                    cv2.rectangle(img, (_x1, _y1), (_x2, _y2), (0, 0, 255), 2)  # 小图片的区域，也在图中标记出来
                    if text == self.value:
                        check_res = True

        # 将标记后的图像编码为base64
        _, buffer = cv2.imencode('.png', img)
        marked_image_base64 = base64.b64encode(buffer).decode('utf-8')
        marked_image_data_url = f"data:image/png;base64,{marked_image_base64}"
        # 添加标记图像到返回结果
        res = {"suc": True, "image": marked_image_data_url, 'res': check_res, 'text': ocr_res}
        return res

    def check(self, task_context: 'TaskContext'):
        """
        检查条件是否满足，条件的判断结果会保存到task_context中，以供下一步使用

        参数:
        - task_context: 任务上下文

        返回:
        - bool: 条件是否满足
        """
        # 这里应该是实际的条件检查逻辑
        # 由于目前没有实现具体的图像识别或文本识别功能，暂时返回True
        # 不同类型的条件应该有不同的检查逻辑
        if self.condition_type == ConditionType.NONE:  # 任意条件都满足
            return True

        pic_base64 = task_context.input_interface.capture().get('image')
        region_base64 = pic_base64
        if self.region_str is not None and self.region_str != '':
            w, h = task_context.input_interface.get_base64_image_size(pic_base64)
            x1, y1, x2, y2 = self.region_str_2_coor(self.region_str, w, h)
            # 裁剪图片
            region_base64 = task_context.input_interface.crop_base64_image(pic_base64, x1, y1, x2, y2)
        else:
            x1, y1, x2, y2 = 0, 0, 0, 0

        if self.condition_type == ConditionType.IMAGE_EXISTS:
            # 检查图像是否存在
            task_context.input_interface.save_base64_to_file(self.value, 'child_pic.png')  # 将文件保存到dm的运行目录下
            pos = task_context.input_interface.find_pic('child_pic.png', x1=x1, y1=y1, x2=x2, y2=y2,
                                                        snapshot=pic_base64)
            if pos.get('exists'):
                w, h = task_context.input_interface.get_base64_image_size(self.value)  # 图片的大小
                lux, luy = pos.get('pos')[0] + x1, pos.get('pos')[1] + y1  # 图片的左上角坐标
                cx, cy = lux + int(w / 2), luy + int(h / 2)  # 图片的中心坐标
                self.position = {'cx': cx, 'cy': cy, 'w': w, 'h': h, 'x': lux, 'y': luy}
                task_context.setLastCondition(self)
                return True
            else:
                self.position = None
                return False
        elif self.condition_type == ConditionType.TEXT_EXISTS:
            # 检查文本是否存在
            res = task_context.input_interface.ocr(region_base64, self.value, score=self.threshold)
            if res is not None:
                if isinstance(res, list):
                    res = res[0]
                _text, box, score = res
                lux, luy = box[0] + x1, box[1] + y1
                w, h = box[2] - box[0], box[3] - box[1]
                cx, cy = lux + int(w / 2), luy + int(h / 2)
                self.position = {"x": lux, "y": luy, "cx": cx, "cy": cy, "w": w, "h": h, "score": score}
                task_context.setLastCondition(self)
                return True
            else:
                return False
        elif self.condition_type == ConditionType.TEXT_EXISTS_EXACT:
            # 检查文本是否存在（精确匹配）
            res = task_context.input_interface.ocr(region_base64, self.value, exact=True, score=self.threshold)
            if res is not None:
                if isinstance(res, list):
                    res = res[0]
                _text, box, score = res
                lux, luy = box[0] + x1, box[1] + y1
                w, h = box[2] - box[0], box[3] - box[1]
                cx, cy = lux + int(w / 2), luy + int(h / 2)
                self.position = {"x": lux, "y": luy, "cx": cx, "cy": cy, "w": box[2] - box[0],
                                 "h": box[3] - box[1], "score": score}
                task_context.setLastCondition(self)
                return True
            else:
                return False
        elif self.condition_type == ConditionType.IMAGE_NOT_EXISTS:
            # 检查图像是否不存在
            task_context.input_interface.save_base64_to_file(self.value, 'child_pic.png')  # 将文件保存到dm的运行目录下
            pos = task_context.input_interface.find_pic('child_pic.png', x1=x1, y1=y1, x2=x2, y2=y2,
                                                        snapshot=pic_base64)
            if pos.get('exists'):
                w, h = task_context.input_interface.get_base64_image_size(self.value)  # 图片的大小
                lux, luy = pos.get('pos')[0] + x1, pos.get('pos')[1] + y1  # 图片的左上角坐标
                cx, cy = lux + int(w / 2), luy + int(h / 2)  # 图片的中心坐标
                self.position = {'cx': cx, 'cy': cy, 'w': w, 'h': h, 'x': lux, 'y': luy}
                task_context.setLastCondition(self)
                return False  # 图片存在，说明条件不满足
            else:
                self.position = None
                return True  # 图片不存在，条件为真
        elif self.condition_type == ConditionType.TEXT_NOT_EXISTS:
            # 检查文本是否不存在
            res = task_context.input_interface.ocr(region_base64, self.value, score=self.threshold)
            if res is not None:
                if isinstance(res, list):
                    res = res[0]
                _text, box, score = res
                lux, luy = box[0] + x1, box[1] + y1
                w, h = box[2] - box[0], box[3] - box[1]
                cx, cy = lux + int(w / 2), luy + int(h / 2)
                self.position = {"x": box[0], "y": box[1], "cx": cx, "cy": cy, "w": box[2] - box[0],
                                 "h": box[3] - box[1], "score": score}
                task_context.setLastCondition(self)
                return False
            else:
                return True
        return True


class ConditionOperator(Enum):
    """条件操作符枚举"""
    AND = "and"  # 与操作
    OR = "or"  # 或操作


@dataclass
class CompositeCondition:
    """
    复合条件类，用于定义复杂的条件组合

    属性说明:
    - conditions: 条件列表
    - operator: 条件操作符（AND或OR）
    """
    conditions: List[Union[TaskCondition, 'CompositeCondition']]
    operator: ConditionOperator = ConditionOperator.AND

    def __init__(self, operator: ConditionOperator = ConditionOperator.AND):
        self.operator = operator
        self.conditions = []

    def define_condition_by_json(self, json_data: dict):
        """
        通过JSON数据定义复合条件

        参数:
        - json_data: 包含复合条件信息的字典数据
        """
        # 设置操作符
        if 'operator' in json_data:
            self.operator = ConditionOperator(json_data['operator'])

        # 处理条件列表
        if 'conditions' in json_data and isinstance(json_data['conditions'], list):
            self.conditions = []
            for condition_data in json_data['conditions']:
                # 判断是简单条件还是复合条件
                if 'operator' in condition_data:
                    # 复合条件
                    condition = CompositeCondition()
                    condition.define_condition_by_json(condition_data)
                else:
                    # 简单条件
                    condition = TaskCondition()
                    condition.define_condition_by_json(condition_data)
                self.conditions.append(condition)

    def check(self, task_context: 'TaskContext'):
        """
        检查复合条件是否满足

        参数:
        - task_context: 任务上下文

        返回:
        - bool: 条件是否满足
        """
        if self.operator == ConditionOperator.AND:
            # 所有条件都必须满足
            for condition in self.conditions:
                if not condition.check(task_context):
                    return False
            return True
        elif self.operator == ConditionOperator.OR:
            # 至少一个条件满足
            for condition in self.conditions:
                if condition.check(task_context):
                    return True
            return False
        return True


class ExecutionAction(Enum):
    """执行动作枚举"""
    LEFT_CLICK = "left_click"  # 鼠标左键点击
    RIGHT_CLICK = "right_click"  # 鼠标右键点击
    MIDDLE_CLICK = "middle_click"  # 鼠标中键点击
    DOUBLE_CLICK = "double_click"  # 鼠标双击
    LEFT_DOWN = "left_down"  # 鼠标左键按下
    LEFT_UP = "left_up"  # 鼠标左键弹起
    RIGHT_DOWN = "right_down"  # 鼠标右键按下
    RIGHT_UP = "right_up"  # 鼠标右键弹起
    MIDDLE_DOWN = "middle_down"  # 鼠标中键按下
    MIDDLE_UP = "middle_up"  # 鼠标中键弹起
    WHEEL_UP = "wheel_up"  # 鼠标滚轮向上
    WHEEL_DOWN = "wheel_down"  # 鼠标滚轮向下
    MOVE_TO = "move_to"  # 鼠标移动
    KEY_PRESS = "key_press"  # 按下键盘按键
    KEY_PRESS_COMBINATION = "key_press_combination"  # 按下键盘组合键
    INPUT_TEXT = "input_text"  # 输入文本SendString
    INPUT_TEXT_IME = "input_text_ime"  # 输入文本SendStringIme
    INPUT_TEXT_IME2 = "input_text_ime2"  # 输入文本SendStringIme2
    INPUT_STRING = "input_string"  # 输入字符串KeyPressStr
    SET_CLIPBOARD = "setClipboard"  # 设置剪切板内容
    SEND_PASTE = "send_paste"  # 粘贴SendPaste
    WAIT = "wait"  # 等待


@dataclass
class ExecutionMethod:
    """
    执行方法类，用于定义任务步骤的执行动作

    属性说明:
    - action: 执行动作类型
    - target: 目标位置（坐标或图像路径）
    - key: 按键值（针对键盘操作）
    - text: 输入文本（针对文本输入操作）
    """
    action: ExecutionAction
    target: str = None  # 坐标或图像路径
    key: str = None  # 按键值
    text: str = None  # 输入文本

    def _eval_expression(self, expr: str, position: dict = None):
        """
        计算表达式的值，例如 "x+10", "cy", "x+w" 等

        :param expr: 表达式字符串
        :param position: 位置字典
        :return: 计算结果
        """
        # 使用正则表达式匹配变量和操作符
        # 匹配形如 "a+b" 或 "a-b" 或 "a" 的表达式
        if position is None:
            position = {}
        match = re.match(r'([a-zA-Z_][a-zA-Z0-9_]*)([+-])(\d+)', expr)
        if match:
            var_name = match.group(1)
            operator = match.group(2)
            offset = int(match.group(3))

            if var_name in position:
                base_value = position[var_name]
                if operator == '+':
                    return base_value + offset
                elif operator == '-':
                    return base_value - offset
        else:
            # 如果没有操作符，直接返回变量值
            if expr in position:
                return position[expr]
            else:
                try:  # 纯数值表达式，如"124", "125+3*20"
                    return int(eval(expr))
                except ValueError:
                    pass

        # 如果无法解析表达式，返回0
        logger.warning(f"无法解析表达式，坐标转换失败：{expr=}，{position=}")
        return 0

    def parseCoor(self, target: str, position: dict = None):
        """
        target是描述坐标的字符串，支持以下几种格式：
        1. "x+10, y+5"，其中x, y的数值是position字典中的值，表示字典中的 (x, y)坐标偏移(10, 5)
        2. "cx, cy+10", 其中cx, cy的数值是position字典中的值，表示字典中(cx, cy)坐标偏移(0, 10)
        3. "x, y, x+w, y+h"，其中x, y, w, h都是position字典中的值，分别表示区域的左上角x,y坐标和区域的宽和高，返回这个范围内的随机一个点

        :param target:
        :param position:
        :return:
        """

        # 移除所有空格
        target = target.replace(' ', '')

        # 情况3: "x, y, x+w, y+h" 格式 - 返回区域内的随机点
        if target.count(',') == 3:
            parts = target.split(',')
            if len(parts) == 4:
                # 解析四个表达式
                x1_expr = parts[0]  # x
                y1_expr = parts[1]  # y
                x2_expr = parts[2]  # x+w
                y2_expr = parts[3]  # y+h

                # 计算表达式的值
                x1 = self._eval_expression(x1_expr, position)
                y1 = self._eval_expression(y1_expr, position)
                x2 = self._eval_expression(x2_expr, position)
                y2 = self._eval_expression(y2_expr, position)

                # 在区域内生成随机点
                random_x = random.randint(min(x1, x2), max(x1, x2))
                random_y = random.randint(min(y1, y2), max(y1, y2))
                return random_x, random_y

        # 情况1和2: "x+10, y+5" 或 "cx, cy+10" 格式
        elif target.count(',') == 1:
            x_expr, y_expr = target.split(',')
            x = self._eval_expression(x_expr, position)
            y = self._eval_expression(y_expr, position)
            return x, y

        # 如果不匹配任何格式，返回None
        return None

    def perform(self, task_context: TaskContext):
        """
        执行动作

        参数:
        - task_context: 任务上下文
        """
        if not task_context.input_interface:
            logger.warning("No input interface available")
            return

        input_interface = task_context.input_interface
        task_context.last_condition = task_context.last_condition or SimpleNamespace(position=None)
        if self.action == ExecutionAction.LEFT_CLICK:
            # 解析目标位置并执行点击
            if self.target:
                # 这里应该解析坐标或查找图像位置
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.left_click(x, y)
        elif self.action == ExecutionAction.RIGHT_CLICK:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.right_click(x, y)
        elif self.action == ExecutionAction.MIDDLE_CLICK:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.middle_click(x, y)
        elif self.action == ExecutionAction.DOUBLE_CLICK:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.left_click(x, y)
                input_interface.left_click(x, y)
        elif self.action == ExecutionAction.LEFT_DOWN:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.left_down(x, y)
        elif self.action == ExecutionAction.LEFT_UP:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.left_up()
        elif self.action == ExecutionAction.RIGHT_DOWN:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.right_down(x, y)
        elif self.action == ExecutionAction.RIGHT_UP:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.right_up()
        elif self.action == ExecutionAction.MIDDLE_DOWN:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.middle_down(x, y)
        elif self.action == ExecutionAction.MIDDLE_UP:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.middle_up()
        elif self.action == ExecutionAction.WHEEL_UP:
            if self.target:
                # 滚轮向上滚动
                input_interface.wheel_up()
        elif self.action == ExecutionAction.WHEEL_DOWN:
            if self.target:
                # 滚轮向下滚动
                input_interface.wheel_down()
        elif self.action == ExecutionAction.MOVE_TO:
            if self.target:
                x, y = self.parseCoor(self.target, task_context.last_condition.position)
                input_interface.move_to(x, y)
        elif self.action == ExecutionAction.KEY_PRESS:
            if self.key:
                input_interface.press_key(self.key)
        elif self.action == ExecutionAction.KEY_PRESS_COMBINATION:
            if self.key:
                # 处理组合键
                input_interface.press_key_combination(self.key)
        elif self.action == ExecutionAction.INPUT_TEXT:
            if self.text:
                input_interface.send_string(self.text)
        elif self.action == ExecutionAction.INPUT_TEXT_IME:
            if self.text:
                input_interface.send_string_ime(self.text)
        elif self.action == ExecutionAction.INPUT_TEXT_IME2:
            if self.text:
                input_interface.send_string_ime2(self.text)
        elif self.action == ExecutionAction.INPUT_STRING:
            if self.text:
                # 只支持标准ASCII可见字符
                input_interface.key_press_str(self.text)
        elif self.action == ExecutionAction.SET_CLIPBOARD:
            if self.text:
                input_interface.set_clipboard(self.text)
        elif self.action == ExecutionAction.SEND_PASTE:
            if self.target:
                input_interface.send_paste()
        elif self.action == ExecutionAction.WAIT:
            if self.target:
                try:
                    wait_time = float(self.target)
                    time.sleep(wait_time)
                except ValueError:
                    logger.error(f"无效的等待时间: {self.target}")
                    time.sleep(1)


class FailureHandle:
    def __init__(self):
        self.action = "repeat_step"
        self.value = None

    def define_failure_by_json(self, json_data: dict):
        self.action = json_data.get("action")
        self.value = json_data.get("value")


class TaskStep:
    """
    任务步骤类，定义自动化任务中的单个步骤

    属性说明:
    - name: 步骤名称
    - execution_conditions: 执行条件（可以是简单条件或复合条件）
    - execution_method: 执行方法
    - expected_results: 预期结果（可以是简单条件或复合条件）
    - timeout: 步骤超时时间
    - failure_handling: 失败处理方式
    - wait_time: 等待时间（针对WAIT_AND_RETRY处理方式）
    - max_retries: 最大重试次数
    - next_step_on_success: 成功时的下一步骤名称
    - next_step_on_failure: 失败时的下一步骤名称
    - next_step_conditions: 基于条件的下一步骤映射
    """

    def __init__(self, name="", index=0):
        self.name = name
        self.index = index
        self.failure_handling: FailureHandle = FailureHandle()
        self.execution_condition: Union[TaskCondition, CompositeCondition, None] = None
        self.execution_methods: List[ExecutionMethod] | None = None  # 一个步骤可以有多个执行操作，如连续按键，按组合键等
        self.expected_result: Union[TaskCondition, CompositeCondition, None] = None
        self.timeout: int = 10
        self.next_step_conditions: list[Union[TaskCondition, CompositeCondition]] = []  # 条件到步骤名称的映射

    def define_step_by_json(self, json_data: dict):
        """
        通过JSON数据定义任务步骤

        参数:
        - json_data: 包含任务步骤信息的字典数据
        """
        # 设置步骤名称
        self.name = json_data.get('name', '') or self.name  # 如果名称未指定，则使用默认名称

        # 处理执行条件
        if json_data.get('executionCondition'):
            execution_condition_data = json_data['executionCondition']
            # 简化处理，实际应转换条件数据
            if execution_condition_data.get('operator') is not None:  # 说明是复合条件，需要按复合条件初始化
                execution_condition = CompositeCondition()
                execution_condition.define_condition_by_json(execution_condition_data)
            else:
                execution_condition = TaskCondition()
                execution_condition.define_condition_by_json(execution_condition_data)
            self.execution_condition = execution_condition

        # 创建执行方法对象列表
        self.execution_methods = []
        if json_data.get('executionMethods'):  # 复数形式，表示多个操作
            execution_methods_data = json_data['executionMethods']
            for method_data in execution_methods_data:
                execution_method = ExecutionMethod(
                    action=ExecutionAction(method_data['action']),
                    target=method_data.get('target', ''),
                    key=method_data.get('key'),
                    text=method_data.get('text')
                )
                self.execution_methods.append(execution_method)

        # 创建预期结果对象
        if json_data.get('expectedResult'):
            expected_results_data = json_data['expectedResult']
            # 处理简单条件
            if isinstance(expected_results_data, dict) and 'conditionType' in expected_results_data:
                self.expected_result = TaskCondition(
                    condition_type=ConditionType(expected_results_data['conditionType']),
                    value=expected_results_data['value']
                )

        if json_data.get('nextStepConditions'):  # 条件跳转，根据不同的条件跳转到不同的步骤
            next_step_conditions_data = json_data['nextStepConditions']
            if self.next_step_conditions is None:
                self.next_step_conditions = []
            for condition_data in next_step_conditions_data:
                condition = TaskCondition()
                condition.define_condition_by_json(condition_data)
                self.next_step_conditions.append(condition)

        # 设置其他属性
        self.timeout = json_data.get('timeout', 10)
        self.failure_handling.define_failure_by_json(json_data.get('failureHandling', {}))

    def deal_condition_jump(self, context):
        context.debug(f"步骤执行成功: {self.name}")
        # 判断是否存在条件跳转
        if self.next_step_conditions is not None and len(self.next_step_conditions) > 0:
            context.debug(f"步骤{self.name}->检查条件跳转")
            for condition in self.next_step_conditions:
                context.debug(f"步骤{self.name}->检查条件跳转->{condition}")
                if condition.check(context):
                    return condition.next_step
            context.debug(f"当前步骤执行成功：{self.name}，但所有的条件跳转都不满足，请检查条件跳转配置")
        return self.index + 1

    def execute(self, context: TaskContext):
        """
        执行当前任务步骤

        参数:
        - task_context: 任务上下文，包含输入接口、中断处理器等

        返回:
        - next_step: 下一步骤的索引或名称
        """
        # 检查执行条件，执行条件满足则开始执行，否则检查预期结果，如果预期结果满足则认为当前步骤执行成功，否则按照失败处理方法进行处理
        context.debug(f"步骤{self.name}->检查执行条件->{self.execution_condition}")
        if self.execution_condition and not self.execution_condition.check(context):
            logger.info(f"当前步骤执行条件不满足: {self.name}")
            # 当前步骤的执行条件不满足时，检查是否满足当前的预期结果，如果满足当前步骤的预期结果，则跳过当前步骤，执行下一个步骤
            if self.expected_result and self.expected_result.check(context):
                logger.info(f"当前步骤的预期结果已满足: {self.name}，跳过当前步骤，执行下一步骤")
                return self.deal_condition_jump(context)
            else:  # 执行条件不满足，也不满足预期结果，则检查失败处理方法
                return self._handle_failure(context)[1]

        # 执行动作
        if self.execution_methods:
            context.debug(f"步骤{self.name}->执行动作")
            for execution_method in self.execution_methods:
                context.debug(f"步骤{self.name}->执行动作->{execution_method}")
                execution_method.perform(context)

        # 检查预期结果
        if self.expected_result:
            context.debug(f"步骤{self.name}->检查预期结果->{self.expected_result}")
            if not self.expected_result.check(context):
                logger.warning(f"当前步骤的预期结果未满足: {self.name}")
                # 根据失败处理方式处理
                return self._handle_failure(context)[1]

        return self.deal_condition_jump(context)

    def _handle_failure(self, context, timeout=90):
        """
        处理步骤执行失败

        参数:
        - context: 任务上下文
        - timeout: 默认的超时时间
        返回:
        - next_step: 下一步骤的索引或名称
        """
        if self.failure_handling.action == "wait_and_retry":  # FailureHandling.WAIT_AND_RETRY:
            time.sleep(int(self.failure_handling.value))
            # 这里应该实现重试逻辑
            if self.expected_result:
                start_time = time.time()
                timeout = getattr(self, 'timeout', 90)  # 默认超时时间120秒
                while time.time() - start_time < timeout:
                    context.debug(f"步骤{self.name}->检查预期结果->{self.expected_result}")
                    if self.expected_result.check(context):
                        break
                    time.sleep(int(self.failure_handling.value))
                return 'step name', self.deal_condition_jump(context)
        elif self.failure_handling.action == "repeat_step":
            return 'step index', self.index  # 重新执行当前步骤
        elif self.failure_handling.action == "raise_error":
            raise StepExecutionError(f"方法执行失败并抛出异常: {self.name}")
        elif self.failure_handling.action == "jump_to_step":
            return 'step name', self.failure_handling.value

        return 'step index', self.index + 1


class StepExecutionError(Exception):
    """步骤执行错误异常"""
    pass


class BaseAutomationTask(ABC):
    """
    基础自动化任务抽象类，定义了自动化任务的基本框架和中断处理机制

    主要功能:
    1. 提供统一的任务执行接口
    2. 集成中断处理机制
    3. 实现超时控制和重试机制
    """

    def __init__(self):
        """
        初始化基础自动化任务

        属性说明:
        - interruption_handler: 中断处理器实例，用于处理任务执行过程中的中断
        - timeout: 任务超时时间（秒），超过此时间任务将被终止
        - poll_interval: 轮询间隔（秒），任务失败后的重试间隔
        """
        self.poll_interval = 1  # 检测间隔
        self.context = TaskContext()
        self.context.set_interruption_handler(InterruptionHandler())
        self.count = []  # 作为任务中计数器，一个任务中可以有多个计数器，该数组的长度就是计数器的数量
        self.timer = []  # 任务中的计时器，一个任务中可以有多个计时器，该数组的长度就是计时器的数量
        self._stop_requested = False

    @abstractmethod
    def execute_main_task(self, *args, **kwargs):
        """
        执行主要任务的抽象方法，需要在子类中实现具体的任务逻辑

        子类必须实现此方法，定义具体的自动化操作步骤
        """
        pass

    def request_stop(self):
        """
        请求停止任务执行
        """
        self._stop_requested = True
        print('停止任务...', self._stop_requested)

    def run_with_interruption_handling(self, *args, **kwargs):
        """
        运行任务并处理中断的核心方法

        执行流程:
        1. 设置任务超时计时器
        2. 在超时时间内循环执行任务
        3. 执行主要任务逻辑
        4. 当主要任务逻辑抛出异常时处理中断
        5. 根据执行结果决定是否继续重试或退出
        """
        # 记录任务开始时间
        # start_time = time.time()
        self._stop_requested = False
        # 任务的执行没有超时时间，因为任务本身就可能是无限循环的，任务的每一个步骤可以有超时时间
        while not self._stop_requested:
            try:
                # 执行主要任务逻辑
                res = self.execute_main_task(*args, **kwargs)
                if not res:
                    logger.warning("任务执行失败")
                break  # 主任务执行成功则退出循环
            except StepExecutionError as e:
                # 捕获步骤执行错误，处理中断
                logger.warning(f"Step execution error: {e}")
                # 处理中断
                self.context.interruption_handler.handle_interruptions()
                # 等待一段时间再重试
                time.sleep(self.poll_interval)
            except Exception as e:
                # 捕获主要任务执行过程中的其他异常
                logger.warning(f"Main task encountered an issue: {e}")
                # 处理中断
                self.context.interruption_handler.handle_interruptions()
                # 等待一段时间再重试
                time.sleep(self.poll_interval)


class ConditionalTask(BaseAutomationTask):
    """
    条件任务类，支持基于条件的任务步骤定义和执行
    """

    def __init__(self, name: str):
        super().__init__()
        self.name = name  # 任务名称
        self.steps: List[TaskStep] = []
        self.task_map: dict = {}  # 任务名称到任务对象的映射
        self.current_task_index: int = 0

    def set_input_interface(self, interface: InputInterface):
        """
        设置输入接口
        """
        self.context.set_input_interface(interface)

    def define_tasks(self, tasks: List[TaskStep]):
        """
        定义任务步骤列表

        参数:
        - tasks: TaskStep对象列表
        """
        self.steps = tasks
        # 构建任务名称到索引的映射
        self.task_map = {task.name: i for i, task in enumerate(tasks)}
        self.current_task_index = 0

    def define_tasks_json(self, task_steps_json: List[dict]):
        """
        从JSON数据定义任务步骤列表

        参数:
        - task_steps_json: 包含任务步骤信息的字典数据列表
        """
        task_steps = []
        # 转换每个步骤数据为TaskStep对象
        for i, step_data in enumerate(task_steps_json):
            # 创建任务步骤对象
            task_step = TaskStep(index=i)  # 临时名称，会在define_step_by_json中更新
            task_step.define_step_by_json(step_data)
            task_steps.append(task_step)

        # 调用原始的define_tasks方法
        self.define_tasks(task_steps)

    def execute_main_task(self, *args, **kwargs):
        """
        执行主要任务逻辑
        """
        if not self.context.input_interface.initialized:
            self.context.printToTerm('InputInterface没有初始化，请初始化后再试', 'warn')
            self.context.setTaskBar('InputInterface没有初始化，请初始化后再试', 0)
            return False

        while 0 <= self.current_task_index < len(self.steps):
            if self._stop_requested:  # 外部的停止执行指令，有其他线程修改为True，则退出任务执行循环
                break
            step = self.steps[self.current_task_index]
            self.context.printToTerm(f"执行步骤：{step.name}", 'debug')
            self.context.setTaskBar(f"执行步骤：{step.name}", 0)
            try:
                next_step = step.execute(self.context)
                self.current_task_index = self._resolve_next_step(next_step)
            except StepExecutionError:
                # 让上层处理中断
                raise
            except Exception as e:
                logger.error(f"执行任务步骤出错，步骤名称为：{step.name}，错误为：{e}")
                raise StepExecutionError(f"Step execution failed: {step.name}") from e
        return True

    def _resolve_next_step(self, next_step):
        """
        解析下一步骤

        参数:
        - next_step: 下一步骤的索引或名称

        返回:
        - int: 下一步骤的索引
        """
        if isinstance(next_step, int):
            return next_step
        elif isinstance(next_step, str):
            return self.task_map.get(next_step, self.current_task_index + 1)  # 找不到就返回下一个步骤
        else:
            return self.current_task_index + 1
